package com.deer.wms.ware.task.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.deer.wms.base.system.service.AreaInfoService;
import com.deer.wms.base.system.service.CellInfoService;
import com.deer.wms.busine.tactic.model.TurnoverTacticDetail;
import com.deer.wms.busine.tactic.model.TurnoverTacticDetailCriteria;
import com.deer.wms.busine.tactic.service.PickTacticService;
import com.deer.wms.busine.tactic.service.TurnoverTacticDetailService;
import com.deer.wms.intercept.common.data.CurrentUser;
import com.deer.wms.inventory.constant.InventoryConstant;
import com.deer.wms.inventory.model.Inventory.Inventory;
import com.deer.wms.inventory.model.Inventory.InventoryCriteria;
import com.deer.wms.inventory.model.Inventory.InventorySelectDto;
import com.deer.wms.inventory.model.Inventory.InventoryTransact;
import com.deer.wms.inventory.service.InventoryService;
import com.deer.wms.inventory.service.InventoryTransactService;
import com.deer.wms.project.root.core.result.CommonCode;
import com.deer.wms.project.root.exception.ServiceException;
import com.deer.wms.project.root.service.SuperServiceImpl;
import com.deer.wms.project.root.util.BeanUtils;
import com.deer.wms.project.root.util.StringUtils;
import com.deer.wms.system.manage.constant.SystemManageConstant;
import com.deer.wms.system.manage.model.user.UserInfo;
import com.deer.wms.system.manage.service.AreaUserService;
import com.deer.wms.system.manage.service.CodeRuleService;
import com.deer.wms.system.manage.service.CrmDeptAreaService;
import com.deer.wms.system.manage.service.UserInfoService;
import com.deer.wms.ware.task.config.StrategyErpService;
import com.deer.wms.ware.task.constant.WareTaskConstant;
import com.deer.wms.ware.task.dao.PickTaskMapper;
import com.deer.wms.ware.task.erp.UpdatePPF;
import com.deer.wms.ware.task.model.SO.SoDetail;
import com.deer.wms.ware.task.model.SO.SoDetailDto;
import com.deer.wms.ware.task.model.SO.SoMaster;
import com.deer.wms.ware.task.model.Wave.WaveDetail;
import com.deer.wms.ware.task.model.Wave.WaveMaster;
import com.deer.wms.ware.task.model.out.OutDetail;
import com.deer.wms.ware.task.model.out.OutMaster;
import com.deer.wms.ware.task.model.pickTask.PickTask;
import com.deer.wms.ware.task.model.pickTask.PickTaskCriteria;
import com.deer.wms.ware.task.model.pickTask.PickTaskDto;
import com.deer.wms.ware.task.model.pickTask.SeedingWallAlertInfo;
import com.deer.wms.ware.task.model.pushErp.beasAssist.BaseAssist;
import com.deer.wms.ware.task.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.activation.DataHandler;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

import static com.deer.wms.project.root.util.CollectionUtils.convertMap;

/**
 * @author guo
 * @since 2020/02/02
 */
@Service
@Transactional
@Slf4j
public class PickTaskServiceImpl extends SuperServiceImpl<PickTaskMapper, PickTask> implements PickTaskService {

    @Autowired
    private PickTaskMapper pickTaskMapper;
    @Autowired
    private PickTaskService pickTaskService;
    @Autowired
    private CodeRuleService codeRuleService;
    @Autowired
    private PickTacticService pickTacticService;
    @Autowired
    private SoDetailService soDetailService;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private InventoryTransactService inventoryTransactService;
    @Autowired
    private SoMasterService soMasterService;
    @Autowired
    private WaveMasterService waveMasterService;
    @Autowired
    private WaveDetailService waveDetailService;
    @Autowired
    private TurnoverTacticDetailService turnoverTacticDetailService;
    @Autowired
    private AreaUserService areaUserService;
    @Autowired
    private AreaInfoService areaInfoService;
    @Autowired
    private OutMasterService outMasterService;
    @Autowired
    private StrategyErpService strategyErpService;

    @Autowired
    private CrmDeptAreaService crmDeptAreaService;


    @Override
    public List<PickTaskDto> findList(PickTaskCriteria criteria) {
        return pickTaskMapper.findList(criteria);
    }




    public List<PickTaskDto> findPageList(PickTaskCriteria criteria) {
        List<PickTaskDto> list = pickTaskMapper.findPageList(criteria);
        List<Integer> soMasterIds = list.stream().map(PickTask::getSoMasterId).collect(Collectors.toList());
        List<String> waveCodes = list.stream().map(PickTask::getWaveCode).collect(Collectors.toList());
        List<Integer> soDetailIds = list.stream().map(PickTask::getSoDetailId).collect(Collectors.toList());
        List<Integer> waveDetailIds = list.stream().map(PickTask::getWaveDetailId).collect(Collectors.toList());
        try {
            Map<Integer, SoMaster> soMasterMap = CompletableFuture.supplyAsync(() -> {
                List<SoMaster> soMasters = soMasterService.listByIds(soMasterIds);
                return convertMap(soMasters, SoMaster::getSoMasterId);
            }).get();
            Map<String, WaveMaster> waveMasterMap = CompletableFuture.supplyAsync(() -> {
                if (ObjectUtil.isNotEmpty(waveCodes)) {
                    List<WaveMaster> waveMasters = waveMasterService.list(new QueryWrapper<WaveMaster>().in("code", waveCodes));
                    return convertMap(waveMasters, WaveMaster::getCode);
                } else {
                    return new HashMap<String, WaveMaster>();
                }
            }).get();
            Map<Integer, SoDetail> soDetailMap = CompletableFuture.supplyAsync(() -> {
                List<SoDetail> soDetails = soDetailService.listByIds(soDetailIds);
                return convertMap(soDetails, SoDetail::getSoDetailId);
            }).get();
            Map<Integer, WaveDetail> waveDetailMap = CompletableFuture.supplyAsync(() -> {
                List<WaveDetail> waveDetails = waveDetailService.listByIds(waveDetailIds);
                return convertMap(waveDetails, WaveDetail::getWaveDetailId);
            }).get();
            for (PickTaskDto pickTaskDto : list) {
                SoMaster soMaster = soMasterMap.get(pickTaskDto.getSoMasterId());
                WaveMaster waveMaster = waveMasterMap.get(pickTaskDto.getWaveCode());
                WaveDetail waveDetail = waveDetailMap.get(pickTaskDto.getWaveDetailId());
                SoDetail soDetail = soDetailMap.get(pickTaskDto.getSoDetailId());
                if (ObjectUtil.isNotEmpty(soMaster)) {
                    pickTaskDto.setBillNo(soMaster.getBillNo());
                    pickTaskDto.setCustomerId(soMaster.getCustomerId());
                    pickTaskDto.setCustomerName(soMaster.getCustomerName());
                }
                if (ObjectUtil.isNotEmpty(waveMaster)) {
                    pickTaskDto.setWaveCode(waveMaster.getCode());
                    pickTaskDto.setAllotUserName(waveMaster.getAllotUserName());
                    pickTaskDto.setAllotTime(waveMaster.getAllotTime());
                }
                if (ObjectUtil.isNotEmpty(waveDetail)) {
                    pickTaskDto.setPickUserName(waveDetail.getPickUserName());
                }
                if (ObjectUtil.isNotEmpty(soDetail)) {
                    pickTaskDto.setSoUserName(soDetail.getPickUserName());
                }
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        return list;
    }
    @Override
    public List<PickTaskDto> findByBillNo(PickTaskCriteria criteria) {
        return pickTaskMapper.findByBillNo(criteria);
    }

    @Override
    public List<PickTaskDto> findByWaveMasterId(PickTaskCriteria criteria) {
        return pickTaskMapper.findByWaveMasterId(criteria);
    }

    @Override
    public List<PickTaskDto> allotTask(PickTaskCriteria criteria) {
        return pickTaskMapper.allotTask(criteria);
    }

    @Override
    public List<PickTaskDto> findFirst(PickTaskCriteria criteria) {
        return pickTaskMapper.findFirst(criteria);
    }

    @Override
    public List<PickTaskDto> findBySo(String soNo) {
        return pickTaskMapper.findBySo(soNo);
    }

    @Override
    public List<PickTaskDto> findByWave(Integer waveId) {
        return pickTaskMapper.findByWave(waveId);
    }

    @Override
    public List<SeedingWallAlertInfo> getSeedingWallAlertInfo(PickTaskCriteria criteria) {
        return pickTaskMapper.getSeedingWallAlertInfo(criteria);
    }

    @Override
    public void createPickTask(Integer soType) {
        //Step1.找到所有待分配的SO明细
        List<SoDetailDto> soDetailDtos = soDetailService.findUndistributedSoDetail(soType);
        //库位id
        Set<Integer> wareIds = soDetailDtos.stream().map(SoDetailDto::getWareId).collect(Collectors.toSet());
        //找到所有待分配id
        Set<String> sobillNos = soDetailDtos.stream().map(SoDetailDto::getBillNo).collect(Collectors.toSet());
        //获取所有soMaster
        List<SoMaster> soMasterList = soMasterService.list(new QueryWrapper<SoMaster>()
                .in("bill_no", sobillNos)
        );
        Map<String, List<SoMaster>> soMasterMap = soMasterList.stream().collect(Collectors.groupingBy(SoMaster::getBillNo));
        //货主Id
        Set<Integer> organizationSet = soDetailDtos.stream().map(SoDetailDto::getOrganizationId).collect(Collectors.toSet());
        //物料编码
        Set<String> codeSet = soDetailDtos.stream().map(SoDetailDto::getItemCode).collect(Collectors.toSet());
        InventoryCriteria inventoryCriteria = new InventoryCriteria();
        //物料编码
        inventoryCriteria.setItemCodes(codeSet);
        //code
        inventoryCriteria.setParam(1003);
        //货主id
        inventoryCriteria.setOrganizationSet(organizationSet);
        //明细id
        inventoryCriteria.setWareIds(wareIds);
        List<InventorySelectDto> canBeOutOfStock = inventoryService.findCanBeOutOfStock(inventoryCriteria);
        List<SoMaster> masterList = new ArrayList<>();
        if (soDetailDtos.size() > 0) {
            soDetailDtos:
            for (SoDetailDto soDetail : soDetailDtos) {
                List<InventorySelectDto> inventorySelectDtos = canBeOutOfStock.stream().filter(
                        item ->
                                item.getItemCode().equals(soDetail.getItemCode()) &&
                                        item.getWareId().equals(soDetail.getWareId()) &&
                                        item.getOrganizationId().equals(soDetail.getOrganizationId())
                ).collect(Collectors.toList());

                //Step4.判断是否足够分配
                // 如果库存不够 直接不给分配
                Double totalCanBeQuantity = 0.0;
                for (InventorySelectDto inventorySelectDto : inventorySelectDtos) {
                    totalCanBeQuantity += inventorySelectDto.getQuantity() - inventorySelectDto.getAllotQuantity() - inventorySelectDto.getFreezeQuantity();
                }
                if (totalCanBeQuantity < soDetail.getOrderQuantity() - soDetail.getAllottedQuantity()) {
                    if (ObjectUtil.isNotEmpty(soMasterMap.get(soDetail.getBillNo()))) {
                        SoMaster soMaster = new SoMaster();
                        soMaster = soMasterMap.get(soDetail.getBillNo()).get(0);
                        soMaster.setState(WareTaskConstant.SO_STATE_OUTOFSTOCK);
                        masterList.add(soMaster);
                    }
//                    SoMaster soMaster = soMasterService.findBy("bill_no", soDetail.getBillNo());

//                    soMasterService.updateById(soMaster);
                    //跳出循环
                    continue;
                }
                //Step5.创建拣货任务
                this.createPickTask(soDetail, inventorySelectDtos);
            }
        }
        soMasterService.updateBatchById(masterList);
        //Step6.FIXME:合并拣货任务  波次
        List<PickTaskDto> mergePickTasks = pickTaskMapper.mergeTask();
        mergePickTasks.forEach(item -> {
            List<PickTask> list = pickTaskMapper.findMergeTask(item);
            // 插入合并任务
            item.setPickTaskId(null);
            Double quantity = 0.0;
            for (PickTask pickTask : list) {
                quantity += pickTask.getPickQuantity();
            }

            item.setPickQuantity(quantity);
            item.setIsParent(1);
            save(item);

            for (PickTask pickTask : list) {
                pickTask.setParentTaskId(item.getPickTaskId());
                pickTask.setState(WareTaskConstant.PICK_STATE_MERGED);
                updateById(pickTask);
            }
            // 关联合并任务ID，并状态改为已合并
            //pickTaskMapper.updateChildTasks(item);
        });

        //FIXME:爆款产品，从验收位置直接生成拣货任务
//        List<Inventory> inventories = inventoryService.findBaoKuan(wareId);
//        for (Inventory in:inventories
//             ) {
//            createBaoKuanPickTask(in);
//        }
    }


    @Override
    public void createPickTask(List<WaveDetail> waveDetailList, UserInfo currentUser) {
        List<PickTask> pickTaskList = new ArrayList<>();
        WaveMaster waveMaster = waveMasterService.getById(waveDetailList.iterator().next().getWaveMasterId());
        if (ObjectUtil.isEmpty(waveMaster)) {
            return;
        }
        List<Integer> areaIds = crmDeptAreaService.listByDeptId(waveMaster.getDeptId());
        if (ObjectUtil.isEmpty(areaIds)) {
            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, "未找到库存，车间未绑定仓库");
        }
        //Step1  准备基础数据，放到内存中
        //波次明细Id
        List<Integer> waveDetailIds = waveDetailList.stream().map(WaveDetail::getWaveDetailId).collect(Collectors.toList());
        //任务单明细List<>
        List<SoDetail> oldSoDetails = soDetailService.list(new QueryWrapper<SoDetail>().in("wave_detail_id", waveDetailIds));
        //排序
        List<SoDetail> soDetails = oldSoDetails.stream().sorted(Comparator.comparing(SoDetail::getSort)).collect(Collectors.toList());
        //任务单号billNo
        Set<String> billNos = soDetails.stream().map(SoDetail::getBillNo).collect(Collectors.toSet());
        //任务单
        List<SoMaster> soMasters = soMasterService.list(new QueryWrapper<SoMaster>().in("bill_no", billNos));
        //转换Map  key:billNo, val:SoMaster
        Map<String, SoMaster> soMasterMap = convertMap(soMasters, SoMaster::getBillNo);
        Map<Integer, WaveDetail> waveDetailMap = convertMap(waveDetailList, WaveDetail::getWaveDetailId);
        //物料编码集合
        Set<String> itemCodes = soDetails.stream().map(SoDetail::getItemCode).collect(Collectors.toSet());
        //Step1.拼接条件(库区，物料)  找到可用容器库存 --这里的库存就是最细的粒度，后续分配的时候也是按容器来分配
        InventoryCriteria inventoryCriteria = new InventoryCriteria();
        inventoryCriteria.setWareId(waveDetailList.get(0).getWareId());
        inventoryCriteria.setItemCodes(itemCodes);
        inventoryCriteria.setAreaIds(areaIds);
        inventoryCriteria.setNotAsnStockIdS(pushErpService.getNotAasnStock());
        inventoryCriteria.setOrganizationId(-1); //自有库存
        //查找库存
        List<InventorySelectDto> inventoryDtos = inventoryService.findCanBeOutOfStock(inventoryCriteria);
        Map<Integer, InventorySelectDto> invMap = convertMap(inventoryDtos, InventorySelectDto::getInventoryId);
        for (SoDetail soDetail : soDetails) {
            SoMaster soMaster = soMasterMap.get(soDetail.getBillNo());
            //查找对应库存
            List<InventorySelectDto> invs = inventoryDtos.stream().filter(item -> soDetail.getItemCode().equals(item.getItemCode())).collect(Collectors.toList());
            //Step2.生成拣货任务
            double remainAllotQty;
            //有预分配数量则取预分配数量，没有则全部分配
            remainAllotQty = soDetail.getOrderQuantity() - soDetail.getAllottedQuantity();
            if (remainAllotQty <= 0) {
                continue;
            }
            for (InventorySelectDto inv : invs) {
                if (inv.getAllotQuantity() <= 0) {
                    inv.setAllotQuantity(0.00);
                }
                double canBeQuantity = inv.getQuantity() - inv.getAllotQuantity() - inv.getFreezeQuantity();
                if (canBeQuantity <= 0) {
                    continue;
                }
                //Step2.1新建拣货任务  根据波次明细初始化
                PickTask pickTask = new PickTask(
                        soDetail.getSoDetailId(),
                        soMaster.getSoMasterId(),
                        soMaster.getWareId(),
                        soMaster.getWareName(),
                        soMaster.getOrganizationId(),
                        soMaster.getOrganizationName(),
                        inv.getAreaCode(),
                        inv.getAreaName(),
                        inv.getShelfCode(),
                        inv.getShelfName(),
                        inv.getCellCode(),
                        inv.getCellName(),
                        inv.getItemCode(),
                        inv.getItemName(),
                        inv.getBatchId(),
                        inv.getBatchName(),
                        inv.getBoxCode(),
                        inv.getLpn(),
                        soDetail.getPackDetailId(),
                        soDetail.getPackDescribe(),
                        0.00,
                        0,
                        inv.getInventoryId(),
                        1,
                        soDetail.getImgUrl()
                );
                pickTask.setPickTaskCode(codeRuleService.generateCodeByRedis(SystemManageConstant.CODE_RULE_PICK));
                //遍历可用库存，分配可用数量 (引申：对于复杂场景，这里可用动态规划的算法)
                WaveDetail waveDetail = waveDetailMap.get(soDetail.getWaveDetailId());
                pickTask.setWaveDetailId(waveDetail.getWaveDetailId());
                pickTask.setWaveCode(waveDetail.getWaveCode());
                if (canBeQuantity >= remainAllotQty) {
                    //1)可分配数量 大于出库任务明细待分配数量 出库任务明细分配完毕
                    pickTask.setPickQuantity(remainAllotQty);
//                    waveDetail.setPickUserId(currentUser.getUserId());
//                    waveDetail.setPickUserName(currentUser.getUserName());
//                    waveDetail.setAllottedTime(LocalDateTime.now());
                    soDetail.setAllottedQuantity(soDetail.getAllottedQuantity() + pickTask.getPickQuantity());
                    //记录波次明细的已分配数量 记录状态为已分配
                    InventorySelectDto invDto = invMap.get(inv.getInventoryId());
                    invDto.setAllotQuantity(invDto.getAllotQuantity() + pickTask.getPickQuantity());
                    invDto.setTransRatio(1.0);

//                    inventoryService.updateById(inv);
                    pickTask.setInventoryId(inv.getInventoryId());
                    pickTaskList.add(pickTask);
                    //分配完成直接跳出循环
                    //记录状态
                    if (ObjectUtil.equal(soDetail.getOrderQuantity(), soDetail.getAllottedQuantity())) {
                        soDetail.setState(WareTaskConstant.WAVE_STATE_ALL_ALOT);
                    } else {
                        soDetail.setState(WareTaskConstant.WAVE_STATE_PICKING);
                    }
                    break;
                } else {
                    //2)可分配数量不够完全分配  则将可分配数量全部分配出去
                    pickTask.setPickQuantity(canBeQuantity);

                    soDetail.setAllottedQuantity(soDetail.getAllottedQuantity() + canBeQuantity);
                    if (ObjectUtil.equal(soDetail.getOrderQuantity(), soDetail.getAllottedQuantity())) {
                        soDetail.setState(WareTaskConstant.WAVE_STATE_ALL_ALOT);
                    } else {
                        soDetail.setState(WareTaskConstant.WAVE_STATE_PICKING);
                    }
//                    updateSoDetail(waveDetail, canBeQuantity);
                    InventorySelectDto invDto = invMap.get(inv.getInventoryId());
                    invDto.setAllotQuantity(invDto.getAllotQuantity() + pickTask.getPickQuantity());
                    invDto.setTransRatio(1.0);
//                    inventoryService.updateById(inv);
                    pickTask.setInventoryId(inv.getInventoryId());
                    pickTaskList.add(pickTask);
                    remainAllotQty -= canBeQuantity;
                }
                //记录状态
                if (ObjectUtil.equal(soDetail.getOrderQuantity(), soDetail.getAllottedQuantity())) {
                    soDetail.setState(WareTaskConstant.WAVE_STATE_ALL_ALOT);
                } else {
                    soDetail.setState(WareTaskConstant.WAVE_STATE_PICKING);
                }
            }

        }


        List<Inventory> updateInv = new ArrayList<>();
        for (InventorySelectDto inventoryDto : inventoryDtos) {
            Inventory inventory = new Inventory();
            BeanUtils.copyProperties(inventoryDto, inventory);
            updateInv.add(inventory);
        }
        inventoryService.updateBatchById(updateInv);

        Map<Integer, List<PickTask>> pickMap = pickTaskList.stream().collect(Collectors.groupingBy(PickTask::getWaveDetailId));
        for (WaveDetail waveDetail : waveDetailList) {
            List<PickTask> pickTasks = pickMap.get(waveDetail.getWaveDetailId());
            if (ObjectUtil.isEmpty(pickTasks)) {
                continue;
            }
//            double allSum = soDetails.stream().filter(item->item.getWaveDetailId().equals(waveDetail.getWaveDetailId())).mapToDouble(SoDetail::getAllottedQuantity).sum();
            double allSum = pickTasks.stream().mapToDouble(PickTask::getPickQuantity).sum();
            waveDetail.setAllottedQuantity(waveDetail.getAllottedQuantity()+allSum);
            waveDetail.setPickUserId(currentUser.getUserId());
            waveDetail.setPickUserName(currentUser.getUserName());
            waveDetail.setAllottedTime(LocalDateTime.now());
            if (allSum == 0) {
                waveDetail.setState(WareTaskConstant.WAVE_STATE_PROCESSING);
            }else if (ObjectUtil.equal(waveDetail.getQuantity(), waveDetail.getAllottedQuantity())) {
                waveDetail.setState(WareTaskConstant.WAVE_STATE_ALL_ALOT);
            } else {
                waveDetail.setState(WareTaskConstant.WAVE_STATE_PICKING);
            }

        }
        //保存生成的拣货任务
        pickTaskService.saveBatch(pickTaskList);
        //更新波次明细状态 和 已分配数量
        waveDetailService.updateBatchById(waveDetailList);
        //更新波次状态
        waveMaster.setState(WareTaskConstant.WAVE_STATE_PICKING);
        waveMasterService.updateById(waveMaster);
        soDetailService.updateBatchById(soDetails);
//        if (true) {
//            throw new ServiceException(CommonCode.UPPER_TACTIC_ERROR);
//        }
        //冻结任务单数量
        this.lockQty(pickTaskList, soDetails, soMasters);
    }

    /**
     * 冻结任务单数量
     * @param pickTasks
     * @param soDetails
     * @param soMasters
     */
    public void lockQty(List<PickTask> pickTasks,List<SoDetail> soDetails,List<SoMaster>  soMasters) {
        List<UpdatePPF> updatePPFS = new ArrayList<>();
        Map<Integer, SoDetail> soDetailMap = convertMap(soDetails, SoDetail::getSoDetailId);
        //
        for (PickTask pickTask : pickTasks) {
            UpdatePPF ppf = new UpdatePPF();
            SoDetail soDetail = soDetailMap.get(pickTask.getSoDetailId());
            //源单id
            ppf.setBillId(soDetail.getErpBillId());
            //行号
            ppf.setRowIndex(soDetail.getBomSplit());
            //数量
            ppf.setCommitQty(pickTask.getPickQuantity());
            updatePPFS.add(ppf);
        }
//        //冻结任务单数量
//        if (ObjectUtil.isNotEmpty(updatePPFS)) {
//            try {
//                pushErpService.UpdatePPFeedAssignedQty(updatePPFS);
//            } catch (Exception e) {
//                System.out.println(e);
//            }
//        }

    }


    public void updateSoDetail(WaveDetail waveDetail, Double qty) {
        List<SoDetail> soDetails = soDetailService.list(new QueryWrapper<SoDetail>().in("wave_detail_id", waveDetail.getWaveDetailId()));
        List<SoDetail> soDetailList = new ArrayList<>();
        for (SoDetail soDetail : soDetails) {
            //可分配数量 = 订单数量  - 分配数量
            double soQty = soDetail.getOrderQuantity() - soDetail.getAllottedQuantity();
            if (soQty == 0.00) {
                continue;
            }
            if (soQty == qty) {
                soDetail.setAllottedQuantity(soQty);
                //完全分配
                soDetail.setState(WareTaskConstant.WAVE_STATE_ALL_ALOT);
                soDetailList.add(soDetail);
                break;
            }
            if (soQty > qty) {
                soDetail.setAllottedQuantity(soDetail.getAllottedQuantity() + qty);
                //部分分配
                soDetail.setState(WareTaskConstant.WAVE_STATE_PICKING);
                soDetailList.add(soDetail);
                break;
            } else {
                //完全分配
                soDetail.setAllottedQuantity(soDetail.getAllottedQuantity() + soQty);
                soDetail.setState(WareTaskConstant.WAVE_STATE_ALL_ALOT);
                //指向改为空
                soDetailList.add(soDetail);
                qty -= soQty;
            }
        }
        //批量修改
        soDetailService.updateBatchById(soDetails);
    }


    @Override
    public void createPickTask(String soMasterBillNo, CurrentUser currentUser, String agreementNo) {
        SoMaster soMaster = soMasterService.getOneByField("bill_no", soMasterBillNo);
        List<SoDetail> soDetails = soDetailService.getListByField("bill_no", soMasterBillNo);
        for (SoDetail soDetail : soDetails) {
            //只需要对未完全分配的进行拣货推荐：计划数大于已分配数
            if (soDetail.getOrderQuantity() > soDetail.getAllottedQuantity()) {
                //找到满足条件的（物料，批次）可用库存
                InventoryCriteria inventoryCriteria = new InventoryCriteria();
                inventoryCriteria.setItemCode(soDetail.getItemCode());
                    //项目需求（带入批次查询）
                inventoryCriteria.setCategoryId(559967);
                inventoryCriteria.setBatchName(agreementNo);
                inventoryCriteria.setWareId(soMaster.getWareId());
                inventoryCriteria.setOrganizationId(soMaster.getOrganizationId());

                List<InventorySelectDto> inventoryDtos = inventoryService.findCanBeOutOfStock(inventoryCriteria);
                // 增加判断这里判断是否有库存--没有库存抛异常(张宇)
                if (inventoryDtos.size() < 1) {
                    // 没有库存
                    // 没有查询到数据
                    continue;
                }
                //创建拣货任务
                if (ObjectUtil.isNotEmpty(agreementNo)) {
                    inventoryDtos = inventoryDtos.stream().sorted(Comparator.comparing(Inventory::getCategoryId)).collect(Collectors.toList());
                }

                this.createPickTask(soMaster, soDetail, inventoryDtos);
            }
        }
    }

    /**
     * 根据单号创建拣货任务
     *
     * @param soMasterBillNo
     * @param currentUser
     * @param agreementNo
     */
    @Override
    public void createPickTaskWin(String soMasterBillNo, CurrentUser currentUser) {
        SoMaster soMaster = soMasterService.getOneByField("bill_no", soMasterBillNo);
        soMasterService.synchronous(Arrays.asList(soMaster));
        List<SoDetail> soDetails = soDetailService.getListByField("bill_no", soMasterBillNo);
        List<Integer> areaIds = soDetails.stream().map(item -> item.getStockId()).collect(Collectors.toList());
        if (ObjectUtil.isEmpty(areaIds)) {
            return;
        }
        for (SoDetail soDetail : soDetails) {
            //只需要对未完全分配的进行拣货推荐：计划数大于已分配数
            if (soDetail.getOrderQuantity() > soDetail.getAllottedQuantity()) {
                //找到满足条件的（物料，批次）可用库存
                InventoryCriteria inventoryCriteria = new InventoryCriteria();
//                if (ObjectUtil.isNotEmpty(areaIds)) {
                    inventoryCriteria.setAreaIds(areaIds);
//                }
                inventoryCriteria.setItemCode(soDetail.getItemCode());
                inventoryCriteria.setWareId(soMaster.getWareId());
                inventoryCriteria.setOrganizationId(soMaster.getOrganizationId());
                List<InventorySelectDto> inventoryDtos = inventoryService.findCanBeOutOfStock(inventoryCriteria);
                // 增加判断这里判断是否有库存--没有库存抛异常(张宇)
                if (inventoryDtos.size() < 1) {
                    continue;
                }

                this.createPickTask(soMaster, soDetail, inventoryDtos);
            }
        }
    }


    /**
     * 根据可用库存，分配生成拣货任务
     *
     * @param soDetail      SO明细
     * @param inventoryDtos 可用库存
     */
    public void createPickTask(SoMaster soMaster, SoDetail soDetail, List<InventorySelectDto> inventoryDtos) {
//        InventoryQuantity:
        for (InventorySelectDto invDto : inventoryDtos) {
            //新建拣货任务
            PickTask pickTask = new PickTask(soDetail.getSoDetailId(), soMaster.getSoMasterId(), soMaster.getWareId(),
                    soMaster.getWareName(), soMaster.getOrganizationId(), soMaster.getOrganizationName(),
                    invDto.getAreaCode(), invDto.getAreaName(), invDto.getShelfCode(),
                    invDto.getShelfName(), invDto.getCellCode(), invDto.getCellName(),
                    invDto.getItemCode(), invDto.getItemName(), invDto.getBatchId(),
                    invDto.getBatchName(), invDto.getBoxCode(), invDto.getLpn(),
                    soDetail.getPackDetailId(), soDetail.getPackDescribe(), 0.0, 0,
                    invDto.getInventoryId(), 2,
                    invDto.getImgUrl()
            );
            pickTask.setSoMasterCode(soMaster.getBillNo());

            //[可用的库存数量]
            double canBeQty = invDto.getQuantity() - invDto.getAllotQuantity() - invDto.getFreezeQuantity();
            //[可用的库存数量]
            double remainAllotQty = soDetail.getOrderQuantity() - soDetail.getAllottedQuantity();
            //判断可分配数量
            if (canBeQty >= remainAllotQty) {
                //1)可用库存大于待分配数量 出库任务明细分配完毕
                pickTask.setPickQuantity(remainAllotQty);
                //记录库存已分配数量
                invDto.setAllotQuantity(invDto.getAllotQuantity() + pickTask.getPickQuantity());
                //记录出库任务的已分配数量
                soDetail.setAllottedQuantity(soDetail.getAllottedQuantity() + pickTask.getPickQuantity());
                invDto.setTransRatio(1.0);

                inventoryService.updateById(invDto);
                save(pickTask);
                soDetailService.updateById(soDetail);
                break;
//                break InventoryQuantity;
            } else {
                //2)可用库存数量不够完全分配  全部分配出去
                if (canBeQty > 0) {
                    pickTask.setPickQuantity(canBeQty);
                    invDto.setAllotQuantity(invDto.getAllotQuantity() + canBeQty);
                    soDetail.setAllottedQuantity(soDetail.getAllottedQuantity() + canBeQty);
                    invDto.setTransRatio(1.0);

                    inventoryService.updateById(invDto);
                    save(pickTask);
                    soDetailService.updateById(soDetail);
                }
            }
        }
        //更新SO的分配状态
        updateSoAllotState(soMaster, soDetail);
    }

    private void updateSoAllotState(SoMaster soMaster, SoDetail soDetail) {
        //回写SO状态
        List<SoDetail> list = soDetailService.findByBillNo(soDetail.getBillNo());
        boolean isAllAllot = true;
        for (SoDetail detail : list) {
            if (detail.getAllottedQuantity() > 0) {
                //部分分配 缺货
                soMaster.setState(WareTaskConstant.SO_STATE_OUTOFSTOCK);
            }
            if (detail.getOrderQuantity() > detail.getAllottedQuantity()) {
                isAllAllot = false;
                break;
            }
        }
        if (isAllAllot) {
            soMaster.setState(WareTaskConstant.SO_STATE_ASSIGNED);
        }
        soMasterService.updateById(soMaster);
    }

    /**
     * 处理库存周转规则 待完成
     *
     * @param soDetail
     */
    private String extracted(SoDetailDto soDetail) {
        StringBuffer sort = new StringBuffer();
        //找到库存周转规则，没有则取系统默认周转规则
        String turnoverTacticCode = soDetail.getTurnoverTacticCode();
        if (turnoverTacticCode == null) {
            turnoverTacticCode = "sys_turnover";
        }
        //周转规则
        List<TurnoverTacticDetail> turnoverTacticDetails = turnoverTacticDetailService.findList(new TurnoverTacticDetailCriteria(turnoverTacticCode));
        if (turnoverTacticDetails.size() > 0) {
            for (int i = 0; i < turnoverTacticDetails.size(); i++) {
                TurnoverTacticDetail turnoverTacticDetail = turnoverTacticDetails.get(i);
                if (turnoverTacticDetail.getBatchTacticDetailCode().equals("productDate")) {
                    sort.append("product_date");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("exDate")) {
                    sort.append("ex_date");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("inDate")) {
                    sort.append("in_date");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("supplierCode")) {
                    sort.append("supplier_code");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("supplierBatch")) {
                    sort.append("supplier_batch");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("qcState")) {
                    sort.append("qc_state");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("costPrice")) {
                    sort.append("cost_price");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("salePrice")) {
                    sort.append("sale_price");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("detailNo")) {
                    sort.append("detail_no");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("memo1")) {
                    sort.append("memo1");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("memo2")) {
                    sort.append("memo2");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("memo3")) {
                    sort.append("memo3");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("batchAttribute1")) {
                    sort.append("batch_attribute_1");
                } else if (turnoverTacticDetail.getBatchTacticDetailCode().equals("batchAttribute2")) {
                    sort.append("batch_attribute_2");
                }
                //判断升序或者降序
                if (turnoverTacticDetail.getSort().equals(1)) {
                    sort.append(" asc,");
                } else {
                    sort.append(" desc,");
                }
//                        if((i+1)==turnoverTacticDetails.size()&&sort.length()>0){
//                            sort.substring(0,sort.lastIndexOf(","));
//                        }
            }
        }
        String sortSql = sort.toString();
        sortSql = sortSql.substring(0, sortSql.lastIndexOf(','));
        return sortSql;
    }

    @Autowired
    private CellInfoService cellInfoService;

    @Override
    public void createBaoKuanPickTask(Inventory inventory, Integer acceptRecordId) {

        //Step1.重新生成库存
        Integer inventoryId = inventory.getInventoryId();
        Inventory newInventory = inventory;
        newInventory.setInventoryId(null);
        inventoryService.save(newInventory);

        //准备数据
        String fromCellCode = inventory.getCellCode();
        String toCellCode = cellInfoService.getSysJhCell(inventory.getWareId()); //获取系统内置的集货库位
        Double quantity = inventory.getQuantity();
        Double allotQuantity = 0.0;
        Double dpAllotQuantity = 0.0;


        List<SoDetailDto> soDetailDtos = soDetailService.findBaoKuanByItemCode(inventory.getItemCode(), inventory.getWareId(), inventory.getOrganizationId());
        if (soDetailDtos.size() > 0) {
            for (SoDetailDto soDetail : soDetailDtos) {
                if (soDetail.getSoStructure() > 1) {
                    dpAllotQuantity += soDetail.getOrderQuantity() - soDetail.getAllottedQuantity();
                    continue;
                }
                if (soDetail.getAllottedQuantity() > 0) {
                    dpAllotQuantity += soDetail.getOrderQuantity() - soDetail.getAllottedQuantity();
                    continue;
                }
                if (quantity - allotQuantity - dpAllotQuantity >= soDetail.getOrderQuantity()) {
                    PickTask pickTask = new PickTask(
                            soDetail.getSoDetailId(),
                            soDetail.getSoMasterId(),
                            soDetail.getWareId(),
                            soDetail.getWareName(),
                            soDetail.getOrganizationId(),
                            soDetail.getOrganizationName(),
                            fromCellCode,
                            inventory.getItemCode(),
                            inventory.getItemName(),
                            inventory.getBatchId(),
                            inventory.getBatchName(),
                            inventory.getBoxCode(),
                            inventory.getLpn(),
                            soDetail.getPackDetailId(),
                            soDetail.getPackDescribe(),
                            soDetail.getOrderQuantity(),
                            2,
                            newInventory.getInventoryId(),
                            2,
                            inventory.getImgUrl()
                    );
                    pickTask.setToCellCode(toCellCode);
                    save(pickTask);
                    soDetail.setAllottedQuantity(pickTask.getPickQuantity());
                    soDetail.setPickTransRatio(1.0);
                    soDetail.setPickQuantity(pickTask.getPickQuantity());
                    soDetail.setState(2);
                    soDetailService.updateById(soDetail);
                    SoMaster soMaster = soMasterService.findBy("bill_no", soDetail.getBillNo());
                    soMaster.setState(6); //全部拣货
                    soMasterService.updateById(soMaster);
                    allotQuantity += pickTask.getPickQuantity();
                }
            }
        }
        if (allotQuantity < 0.1) {
            inventoryService.removeById(newInventory.getInventoryId());
            return;
        }


        newInventory.setCellCode(toCellCode);
        //newInventory.setAllotQuantity(allotQuantity);
        newInventory.setQuantity(allotQuantity);
        newInventory.setBaoKuan(acceptRecordId);
        inventoryService.updateById(newInventory);

        if (quantity - allotQuantity < 0.1) {
            inventoryService.removeById(inventoryId);
        } else {
            inventory.setInventoryId(inventoryId);
            inventory.setCellCode(fromCellCode);
            inventory.setQuantity(quantity - allotQuantity);
            inventory.setAllotQuantity(0.0);
            inventory.setBaoKuan(0);
            inventoryService.updateById(inventory);
        }


    }

    @Override
    public List<PickTask> findBySoDetailId(Integer soDetailId) {
        return pickTaskMapper.findBySoDetailId(soDetailId);
    }

    @Override
    public List<PickTaskDto> getChildTasksByParentId(Integer parentId) {
        return pickTaskMapper.getChildTasksByParentId(parentId);
    }

    @Override
    public PickTaskDto getTaskById(Integer taskId) {
        return pickTaskMapper.getTaskById(taskId);
    }

    @Override
    public List<PickTaskDto> findBySoMasterId(Integer soMaterId) {
        return pickTaskMapper.findBySoMasterId(soMaterId);
    }

    @Override
    public void commitPick(PickTask pickTask, CurrentUser currentUser) {
        //Step1.生成库存事务
        InventoryTransact inventoryTransact = toStockRequest(pickTask, currentUser);
        //Step2.运行提交库存事务
        int ivtId = inventoryTransactService.run(inventoryTransact);
        //Step3.更新拣货任务状态
        pickTask.setState(WareTaskConstant.PICK_STATE_ALLPICK);
        pickTask.setPickTime(DateUtil.now());
        pickTask.setPickUserId(currentUser.getUserId());
        pickTask.setPickUserName(currentUser.getUserName());
        pickTask.setInventoryId(ivtId);
        pickTask.setSeededQuantity(pickTask.getPickQuantity());


        this.updateById(pickTask);
        //这里可能是波次拣货或者是按单拣货
        Inventory inventory = inventoryService.getById(ivtId);
        if (ObjectUtil.isEmpty(pickTask.getWaveCode())) {
            //Step4.库存上记录SO单据号
            SoMaster soMaster = soMasterService.getById(pickTask.getSoMasterId());
            inventory.setBillNo(soMaster.getBillNo());
            //更新SO的状态
            SoDetail soDetail = soDetailService.getById(pickTask.getSoDetailId());
            if (ObjectUtil.equals(soDetail.getPickQuantity(), soDetail.getOrderQuantity())) {
                soDetail.setState(WareTaskConstant.SO_DETAIL_STATE_ALL_PICK);
            } else {
                soDetail.setState(WareTaskConstant.SO_DETAIL_STATE_PART_PICK);
            }
            //更新SO

            //回传ERP
//            pickSoSendErp(inventory, pickTask);

        } else {
            //波次处理逻辑
            WaveDetail waveDetail = waveDetailService.getById(pickTask.getWaveDetailId());
            waveDetail.setPickQuantity(waveDetail.getPickQuantity() + pickTask.getPickQuantity());
            //判断是否已拣货完毕
            if (waveDetail.getPickQuantity().equals(waveDetail.getQuantity())) {
                waveDetail.setState(WareTaskConstant.WAVE_STATE_CLOSE);
            }
            waveDetailService.updateById(waveDetail);
            inventory.setBillNo(waveDetail.getWaveDetailCode());

        }
        this.updateSOState(pickTask.getSoDetailId(), pickTask.getPickQuantity());

        inventoryService.updateById(inventory);
    }


    private void pickWaveSendErp(Inventory inventory, WaveDetail waveDetail) {
        Set<OutDetail> outDetailSet = new HashSet<>();
        QueryWrapper<SoDetail> soDetailQw = new QueryWrapper<>();
        // //08.04.0389
        soDetailQw.eq("wave_detail_id", waveDetail.getWaveDetailId());
        List<SoDetail> soDetails = soDetailService.list(soDetailQw);
        if (ObjectUtil.isEmpty(soDetails)) {
            SoDetail soDetail = soDetailService.getById(waveDetail.getSoDetailId());
            soDetails.add(soDetail);
        }


//        Set<SoDetail> soDetailSet = new HashSet<>(soDetails);
        ArrayDeque<SoDetail> ad = new ArrayDeque<>(soDetails);

        List<Inventory> allotInvs = new ArrayList<>();

        allotInvs.add(inventory);
        //分配库存
        //多个库存对应一个波次明细   一个波次明细对应多个出库任务明细
        //  A1, A2, A3   ===>   B1
        //  B1  ===>  C1,C2,C3,C4,C5
        Set<OutDetail> extracted = extracted(ad, allotInvs, soDetails);
        outDetailSet.addAll(extracted);
        String billNo = soDetails.get(0).getBillNo();
        double sum = extracted.stream().mapToDouble(OutDetail::getDeliveryQuantity).sum();
        Double quantity = inventory.getQuantity();
        log.info("库存扣减数量{},拣货数量{}", quantity, sum);
        SoMaster soMaster = soMasterService.getOneByField("bill_no", billNo);
        Set<OutDetail> collect = outDetailSet.stream().filter(item -> item.getDeliveryQuantity() > 0).collect(Collectors.toSet());
//        if (true) {
//            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR);
//        }
//        strategyErpService.resolveSo(soMaster, collect, new BaseAssist());
    }

    private void pickSoSendErp(Inventory inventory, PickTask pickTask) {

    }

    private Set<OutDetail> extracted(ArrayDeque<SoDetail> queue, List<Inventory> allotInvs, List<SoDetail> soDetailSet) {


        Set<OutDetail> outSet = new HashSet<>(queue.size());
        Set<SoDetail> set = new HashSet<>(queue.size());
//        List<OutDetail>
        for (Inventory allotInv : allotInvs) {
            Double invQty = allotInv.getQuantity();

            while (!queue.isEmpty()) {

                OutDetail outDetail = new OutDetail();
                //取出队列的第一个元素
                SoDetail soDetail = queue.getFirst();

                //SO待填补的数量
                double soQty = soDetail.getAllottedQuantity() - soDetail.getPickQuantity();
                if (soQty == 0) {
                    queue.poll();
                    continue;
                }
                if (invQty < soQty) {
                    //库存不够分配，分配后直接跳出循环
                    soDetail.setPickQuantity(soDetail.getPickQuantity() + invQty);
                    soDetail.setState(WareTaskConstant.SO_DETAIL_STATE_PART_PICK);
                    set.add(soDetail);
                    BeanUtils.copyProperties(soDetail, outDetail);
                    //库存不够分配，回传数量等于库存数量
                    outDetail.setDeliveryQuantity(invQty);
                    outDetail.setCellCode(allotInv.getCellCode());
                    outDetail.setBatchName(allotInv.getBatchName());
                    outDetail.setErpAreaId(allotInv.getErpAreaId());
                    outSet.add(outDetail);
                    soDetailSet.add(soDetail);
                    break;
                } else {
                    //库存足够分配，全部填满SO的数量
                    //未回传ERP数量 +历史数量
                    soDetail.setPickQuantity(soQty + soDetail.getPickQuantity());
                    soDetail.setState(WareTaskConstant.SO_DETAIL_STATE_ALL_SEND);
                    BeanUtils.copyProperties(soDetail, outDetail);

                    outDetail.setDeliveryQuantity(soQty);
                    outDetail.setBoxCode(allotInv.getBoxCode());
                    outDetail.setCellCode(allotInv.getCellCode());
                    outDetail.setBatchName(allotInv.getBatchName());
                    outDetail.setErpAreaId(allotInv.getErpAreaId());
                    outSet.add(outDetail);
                    set.add(soDetail);
                    soDetailSet.add(soDetail);

                    if (outDetail.getDeliveryQuantity() == 0.00) {
                        System.out.println("222222");
                    }

                    //扣减已分配的库存数量
                    invQty -= soQty;
                    //队列第一个元素被分配完毕，出队列
                    queue.poll();
                }
            }
        }
        soDetailService.updateBatchById(set);
        return outSet;

    }


    @Autowired
    private OutDetailService outDetailService;
    @Autowired
    private PushErpService pushErpService;

    @Override
    public OutMaster pickAndSendOut(List<PickTask> pickTasks, CurrentUser currentUser, Integer erpAreaId) {
        OutMaster outMaster = null;
        for (PickTask pickTask : pickTasks) {
            pickTask.setToBoxCode(codeRuleService.generateCellCodeRedis());
//            String toCellCode = cellInfoService.getSysJhCell(erpAreaId);
            pickTask.setToCellCode(cellInfoService.findErpAreaId(erpAreaId));
            commitPick(pickTask, currentUser);
        }
        List<Integer> pickIds = pickTasks.stream().map(item -> item.getPickTaskId()).collect(Collectors.toList());
        List<PickTask> returnPick = pickTaskService.listByIds(pickIds);
        //获取任务单明细
        Set<Integer> soDetailIds = returnPick.stream().map(PickTask::getSoDetailId).collect(Collectors.toSet());
        List<SoDetail> soDetails = soDetailService.listByIds(soDetailIds);
        Map<Integer, SoDetail> soDetailMap = convertMap(soDetails, SoDetail::getSoDetailId);
        List<Integer> invIds = returnPick.stream().map(PickTask::getInventoryId).collect(Collectors.toList());
        List<Inventory> inventories = inventoryService.listByIds(invIds);
        Map<Integer, Inventory> invMap = convertMap(inventories, Inventory::getInventoryId);
        List<OutDetail> outDetails = new ArrayList<>();
        List<SoDetail> updateSoDetail = new ArrayList<>();
        //回传ERP
        for (PickTask pickTaskDto : returnPick) {
            pickTaskDto.setState(5);
            Inventory inventory = invMap.get(pickTaskDto.getInventoryId());
            OutDetail outDetail = new OutDetail();
            SoDetail soDetail = soDetailMap.get(pickTaskDto.getSoDetailId());
            if (ObjectUtil.isEmpty(soDetail)) {
                continue;
            }
            //拣货数量等于分配数量+任务单数量
            soDetail.setDeliveryQuantity(soDetail.getDeliveryQuantity() + pickTaskDto.getPickQuantity());
            if (ObjectUtil.equals(soDetail.getPickQuantity(), soDetail.getOrderQuantity())) {
                soDetail.setState(WareTaskConstant.SO_DETAIL_STATE_ALL_PICK);
            } else {
                soDetail.setState(WareTaskConstant.SO_DETAIL_STATE_PART_PICK);
            }
            //拣货 时间
            soDetail.setPickTime(LocalDateTime.now());
            updateSoDetail.add(soDetail);
            BeanUtils.copyProperties(soDetail, outDetail);
            outDetail.setDeliveryQuantity(pickTaskDto.getPickQuantity()); //数量
            outDetail.setCellCode(inventory.getCellCode());
            outDetail.setBatchName(inventory.getBatchName());
            outDetail.setErpAreaId(inventory.getErpAreaId());
            outDetail.setInvId(inventory.getInventoryId());
            outDetails.add(outDetail);
        }
        pickTaskService.updateBatchById(returnPick);
        SoMaster soMaster = soMasterService.getOneByField("bill_no", outDetails.iterator().next().getBillNo());
        //批量修改任务单详情状态
        soDetailService.updateBatchById(updateSoDetail);
        BaseAssist baseAssist = new BaseAssist();
        baseAssist.setStockId2(erpAreaId);
        if (ObjectUtil.isNotEmpty(soMaster.getBillType())) {
            strategyErpService.resolveSo(soMaster, outDetails, baseAssist);
        } else {
            switch (soMaster.getSoType()) {
                //调拨单
                case 41:
                    soMaster.setSoType(102);
                    break;
            }
            strategyErpService.resolveSo(soMaster, outDetails, baseAssist);
        }
        inventoryService.removeByIds(invIds);

        return outMaster;
    }


    @Override
    @Transactional
    public void pickTask() {
        List<PickTask> pickTasks = list(new QueryWrapper<PickTask>().eq("state", 2));
        for (PickTask pickTask : pickTasks) {
            Inventory inventory = inventoryService.getById(pickTask.getInventoryId());
            WaveDetail waveDetail = waveDetailService.getById(pickTask.getWaveDetailId());
            if (ObjectUtil.isNotEmpty(waveDetail) && ObjectUtil.isNotEmpty(inventory)) {

                inventory.setBillNo(waveDetail.getWaveDetailCode());
                inventoryService.updateById(inventory);
            }

        }
    }

    @Override
    public void updateWaveDetail(PickTask pickTask) {
        Integer waveDetailId = pickTask.getWaveDetailId();
        WaveDetail waveDetail = waveDetailService.getById(waveDetailId);
        List<SoDetail> soDetails = soDetailService.list(new QueryWrapper<SoDetail>().eq("wave_detail_id", waveDetailId));
        double pickQuantity = soDetails.stream().mapToDouble(SoDetail::getPickQuantity).sum();
        double orderQuantity = soDetails.stream().mapToDouble(SoDetail::getOrderQuantity).sum();
        double allQuantity = soDetails.stream().mapToDouble(SoDetail::getAllottedQuantity).sum();
        waveDetail.setQuantity(orderQuantity);
        waveDetail.setPickQuantity(pickQuantity);
        waveDetail.setAllottedQuantity(allQuantity);
        if (ObjectUtil.equals(waveDetail.getQuantity(), waveDetail.getPickQuantity())) {
            waveDetail.setState(WareTaskConstant.SO_DETAIL_STATE_ALL_PICK);
        } else {
            waveDetail.setState(WareTaskConstant.SO_DETAIL_STATE_PART_PICK);
        }
    }

    /**
     * 查询总数
     *
     * @param criteria
     * @return
     */
    @Override
    public long findCount(PickTaskCriteria criteria) {
        return baseMapper.findCount(criteria);
    }


    @Autowired
    private UserInfoService userInfoService;
    /**
     * 波次拣货
     *
     * @param criteria
     * @param currentUser
     */
    @Override
    public void sendWavePick(PickTaskCriteria criteria, CurrentUser currentUser) {
        List<Integer> waveIds = criteria.getWaveIds();
        if (ObjectUtil.isEmpty(currentUser)) {
            throw new ServiceException(CommonCode.SERVER_INERNAL_ERROR, "当前用户已过期，请重新登陆");
        }
        Integer userId = currentUser.getUserId();
        UserInfo userInfo = userInfoService.getById(userId);
        Integer waveCount = Optional.ofNullable(userInfo.getWaveCount()).orElse(3);

        if (waveIds.size() > waveCount) {
            throw new ServiceException(CommonCode.AREA_EXISTS, "拣货数量不可大于" + waveCount);
        }
        List<PickTask> list = pickTaskService.list(new QueryWrapper<PickTask>()
                .in("wave_detail_id", criteria.getWaveIds())
                .eq("state", 0));
        for (PickTask pickTask : list) {
            if (WareTaskConstant.PICK_STATE_ALLPICK.equals(pickTask.getState()) || WareTaskConstant.PICK_STATE_OUTED.equals(pickTask.getState())) {
                continue;
            }
            pickTask.setToBoxCode(codeRuleService.generateCellCodeRedis());
            // 目标库位
            String toCellCode = cellInfoService.getSysJhCell(criteria.getWareId());
            pickTask.setToCellCode(toCellCode);
            pickTask.setReviewQuantity(pickTask.getPickQuantity());
        }
        this.commitPickWare(list, currentUser);
    }


    private void commitPickWare(List<PickTask> list, CurrentUser currentUser) {
        //异步生产库存事务
        CompletableFuture.runAsync(() -> {
            toStockRequestBatch(list, currentUser);
        });
        List<Integer> invIds = list.stream().map(PickTask::getInventoryId).collect(Collectors.toList());
        List<Integer> waveDetailIds = list.stream().map(PickTask::getWaveDetailId).collect(Collectors.toList());
        List<Integer> soDetailIds = list.stream().map(item -> item.getSoDetailId()).collect(Collectors.toList());
        List<WaveDetail> waveDetails = waveDetailService.listByIds(waveDetailIds);
        List<SoDetail> soDetails = soDetailService.listByIds(soDetailIds);
        List<Inventory> inventoryList = inventoryService.listByIds(invIds);
        Map<Integer, WaveDetail> waveDetailMap = convertMap(waveDetails, WaveDetail::getWaveDetailId);
        Map<Integer, SoDetail> soDetailMap = convertMap(soDetails, SoDetail::getSoDetailId);
        Map<Integer, Inventory> invMap = convertMap(inventoryList, Inventory::getInventoryId);
        List<OutDetail> outDetails = new ArrayList<>();
        for (PickTask pickTask : list) {
            OutDetail outDetail = new OutDetail();

            Inventory inventory = invMap.get(pickTask.getInventoryId());
            inventory.setQuantity(inventory.getQuantity() - pickTask.getPickQuantity());
            inventory.setAllotQuantity(inventory.getAllotQuantity() - pickTask.getPickQuantity());
            inventory.setAllotTransRatio(1.0);
            pickTask.setState(WareTaskConstant.PICK_STATE_OUTED);
            pickTask.setPickTime(DateUtil.now());
            pickTask.setPickUserId(currentUser.getUserId());
            pickTask.setPickUserName(currentUser.getUserName());
            pickTask.setSeededQuantity(pickTask.getPickQuantity());
            //处理波次
            WaveDetail waveDetail = waveDetailMap.get(pickTask.getWaveDetailId());
            waveDetail.setPickQuantity(waveDetail.getPickQuantity() + pickTask.getPickQuantity());
            //判断是否已拣货完毕
            if (waveDetail.getPickQuantity().equals(waveDetail.getQuantity())) {
                waveDetail.setState(WareTaskConstant.WAVE_STATE_CLOSE);
            }
            //处理任务单
            SoDetail soDetail = soDetailMap.get(pickTask.getSoDetailId());
            Double pickQuantity = pickTask.getPickQuantity();
            soDetail.setPickQuantity(soDetail.getPickQuantity() + pickQuantity);
            soDetail.setDeliveryQuantity(soDetail.getDeliveryQuantity() + pickTask.getPickQuantity());
            soDetail.setPickTime(LocalDateTime.now());
            BeanUtils.copyProperties(soDetail, outDetail);
            outDetail.setDeliveryQuantity(pickTask.getPickQuantity()); //数量
            outDetail.setCellCode(inventory.getCellCode());
            outDetail.setBatchName(inventory.getBatchName());
            outDetail.setErpAreaId(inventory.getErpAreaId());
            outDetails.add(outDetail);
        }
        //波次
        waveDetailService.updateBatchById(waveDetails);
        //库存
        List<Integer> removeIds = inventoryList.stream().filter(item -> item.getQuantity() == 0).map(Inventory::getInventoryId).collect(Collectors.toList());
        //修改库存
        inventoryService.updateBatchById(inventoryList);
        pickTaskService.updateBatchById(list);
        //删除脏数据
        inventoryService.removeByIds(removeIds);
        soDetailService.updateBatchById(soDetails);
        Set<String> billNos = outDetails.stream().map(OutDetail::getBillNo).collect(Collectors.toSet());
        SoMaster soMaster = soMasterService.getOneByField("bill_no", outDetails.iterator().next().getBillNo());
        List<SoMaster> soMasterList = new ArrayList<>();


        //齐套
        CompletableFuture.runAsync(() -> {
            if (soMaster.getSoType() == 24) {
                List<SoMaster> soMasters = soMasterService.list(new QueryWrapper<SoMaster>().in("bill_no", billNos));
                Map<String, SoMaster> soMasterMap = convertMap(soMasters, SoMaster::getBillNo);
                soMasterMap.forEach((key, val) -> {
                    if (ObjectUtil.isEmpty(val)) {
                        return;
                    }
                    if (soMasterService.dispatch(val.getOrderBillNo())&&val.getDispatch()!=2) {
                        val.setDispatch(1);
                    }
                    else {
                        val.setDispatch(0);
                    }
                });
                soMasterList.addAll(soMasterMap.values());
            }
            soMasterService.updateBatchById(soMasterList);
        });

        strategyErpService.resolveSo(soMaster, outDetails, new BaseAssist());
    }


    private void toStockRequestBatch(List<PickTask> list, CurrentUser currentUser) {
        List<InventoryTransact> inventoryTransacts = new ArrayList<>();
        for (PickTask pickTask : list) {
            InventoryTransact inventoryTransact = new InventoryTransact();
            //Step1.准备数据
            //仓库
            inventoryTransact.setWareId(pickTask.getWareId());
            inventoryTransact.setWareName(pickTask.getWareName());
            //货主
            inventoryTransact.setFromOrganizationId(pickTask.getOrganizationId());
            inventoryTransact.setFromOrganizationName(pickTask.getOrganizationName());
            inventoryTransact.setToOrganizationId(pickTask.getOrganizationId());
            inventoryTransact.setToOrganizationName(pickTask.getOrganizationName());
            //库位
            inventoryTransact.setFromCellCode(pickTask.getCellCode());
            inventoryTransact.setToCellCode(pickTask.getToCellCode());
            //容器
            inventoryTransact.setFromBoxCode(pickTask.getBoxCode());
            inventoryTransact.setToBoxCode(pickTask.getToBoxCode());
            //物料
            inventoryTransact.setItemCode(pickTask.getItemCode());
            inventoryTransact.setItemName(pickTask.getItemName());
            inventoryTransact.setImgUrl(pickTask.getImgUrl());
            //批次
            inventoryTransact.setFromBatchId(pickTask.getBatchId());
            inventoryTransact.setFromBatchName(pickTask.getBatchName());
            inventoryTransact.setToBatchId(pickTask.getBatchId());
            inventoryTransact.setToBatchName(pickTask.getBatchName());

            //数量
            inventoryTransact.setFromQuantity(pickTask.getPickQuantity());
            inventoryTransact.setToQuantity(pickTask.getPickQuantity());
            //包装 转换率
            inventoryTransact.setFromPackDetailId(pickTask.getPackDetailId());
            inventoryTransact.setFromPackDescribe(pickTask.getPackDescribe());
            inventoryTransact.setToPackDetailId(pickTask.getPackDetailId());
            inventoryTransact.setToPackDescribe(pickTask.getPackDescribe());
            inventoryTransact.setFromTransRatio(1.0);
            inventoryTransact.setToTransRatio(1.0);
            //创建人 时间
            inventoryTransact.setCreateUserId(currentUser.getUserId());
            inventoryTransact.setCreateUserName(currentUser.getUserName());
            inventoryTransact.setCreateTime(DateUtil.now());
            //库存id
            inventoryTransact.setToInventoryId(pickTask.getInventoryId());

            //事务类型 这个字段很重要
//        if (StringUtils.isEmpty(pickTask.getToBoxCode())) {
            //整箱拣货
//            inventoryTransact.setTransactType(InventoryConstant.TRANSACT_TYPE_PICK);
//        } else {
            inventoryTransact.setTransactType(InventoryConstant.TRANSACT_TYPE_PICK2);
//        }
            //来源单据号
            //记录单据号
            if (ObjectUtil.isNotNull(pickTask.getSoMasterId())) {
                SoMaster soMaster = soMasterService.getById(pickTask.getSoMasterId());
                inventoryTransact.setBillNo(soMaster.getBillNo());
            }
            inventoryTransacts.add(inventoryTransact);
        }
        inventoryTransactService.saveBatch(inventoryTransacts);
    }


//        pickTaskService.updateBatchById(pickTasks);


//        List<String> boxCode = pickTasks.stream().map(PickTask::getBoxCode).collect(Collectors.toList());
//        List<Inventory> inventories = inventoryService.list(new QueryWrapper<Inventory>().in("box_code", boxCode));


//        List<Inventory> list = inventoryService.list(
//                new QueryWrapper<Inventory>().ne("cell_code","285000")
//
//        );
//
//        List<Inventory> oldInv = list.stream().filter(item -> (ObjectUtil.isEmpty(item.getCellCode())
//                || !item.getCellCode().equals("285000") )&&ObjectUtil.isNotEmpty(item.getBoxCode())
//        ).collect(Collectors.toList());
//        Map<String, List<Inventory>> collect = oldInv.stream().collect(Collectors.groupingBy(Inventory::getBoxCode));
//
//        List<Integer> collect1 = oldInv.stream().map(Inventory::getInventoryId).collect(Collectors.toList());
//        List<Inventory> addList = new ArrayList<>();
//        collect.forEach((key,val)->{
//            Inventory inventory =val.get(0);
//            double sum = val.stream().mapToDouble(Inventory::getQuantity).sum();
//            inventory.setQuantity(sum);
//            addList.add(inventory);
//        });
//        inventoryService.removeByIds(collect1);
//        inventoryService.saveBatch(addList);
//
//
//        for (PickTask pickTask : pickTasks) {
//            Inventory inventory = inventoryService.getOne(new QueryWrapper<Inventory>().eq("box_code", pickTask.getBoxCode()
//
//            ) .eq("item_code",pickTask.getItemCode())    );
//            if (ObjectUtil.isEmpty(inventory)) {
//                waveDetailService.removeById(pickTask.getWaveDetailId());
//                removeById(pickTask.getPickTaskId());
//            } else {
//                inventory.setAllotQuantity(pickTask.getPickQuantity());
//                inventoryService.updateById(inventory);
//            }
//
//        }


    /**
     * 更新SO的状态
     *
     * @param billDetailId SO明细id
     * @param quantity     数量
     */
    private void updateSOState(Integer billDetailId, Double quantity) {
        SoDetail soDetail = soDetailService.getById(billDetailId);
        //计划数量
        Double orderQuantity = soDetail.getOrderQuantity();
        //拣货数量
        soDetail.setPickQuantity(soDetail.getPickQuantity() + quantity);
        if (orderQuantity > soDetail.getPickQuantity()) {
            soDetail.setState(WareTaskConstant.SO_DETAIL_STATE_PART_PICK);
        } else {
            soDetail.setState(WareTaskConstant.SO_DETAIL_STATE_ALL_PICK);
        }
        soDetailService.updateById(soDetail);

        SoMaster soMaster = soMasterService.findBy("bill_no", soDetail.getBillNo());
        List<SoDetail> list = soDetailService.findByBillNo(soDetail.getBillNo());
        Boolean isEnd = true;
        for (SoDetail detail : list) {
            if (ObjectUtil.notEqual(WareTaskConstant.SO_DETAIL_STATE_ALL_PICK, detail.getState())) {
                isEnd = false;
                break;
            }
        }
        if (isEnd) {
            soMaster.setState(WareTaskConstant.SO_STATE_PICKED);
            soMasterService.updateById(soMaster);
        }
    }

    /**
     * 拣货任务生成库存事务
     *
     * @param pickTask    拣货任务
     * @param currentUser 当前用户
     * @return InventoryTransact库存事务
     */
    private InventoryTransact toStockRequest(PickTask pickTask, CurrentUser currentUser) {
        InventoryTransact inventoryTransact = new InventoryTransact();
        //Step1.准备数据
        //仓库
        inventoryTransact.setWareId(pickTask.getWareId());
        inventoryTransact.setWareName(pickTask.getWareName());
        //货主
        inventoryTransact.setFromOrganizationId(pickTask.getOrganizationId());
        inventoryTransact.setFromOrganizationName(pickTask.getOrganizationName());
        inventoryTransact.setToOrganizationId(pickTask.getOrganizationId());
        inventoryTransact.setToOrganizationName(pickTask.getOrganizationName());
        //库位
        inventoryTransact.setFromCellCode(pickTask.getCellCode());
        inventoryTransact.setToCellCode(pickTask.getToCellCode());
        //容器
        inventoryTransact.setFromBoxCode(pickTask.getBoxCode());
        inventoryTransact.setToBoxCode(pickTask.getToBoxCode());
        //物料
        inventoryTransact.setItemCode(pickTask.getItemCode());
        inventoryTransact.setItemName(pickTask.getItemName());
        inventoryTransact.setImgUrl(pickTask.getImgUrl());
        //批次
        inventoryTransact.setFromBatchId(pickTask.getBatchId());
        inventoryTransact.setFromBatchName(pickTask.getBatchName());
        inventoryTransact.setToBatchId(pickTask.getBatchId());
        inventoryTransact.setToBatchName(pickTask.getBatchName());

        //数量
        inventoryTransact.setFromQuantity(pickTask.getPickQuantity());
        inventoryTransact.setToQuantity(pickTask.getPickQuantity());
        //包装 转换率
        inventoryTransact.setFromPackDetailId(pickTask.getPackDetailId());
        inventoryTransact.setFromPackDescribe(pickTask.getPackDescribe());
        inventoryTransact.setToPackDetailId(pickTask.getPackDetailId());
        inventoryTransact.setToPackDescribe(pickTask.getPackDescribe());
        inventoryTransact.setFromTransRatio(1.0);
        inventoryTransact.setToTransRatio(1.0);
        //创建人 时间
        inventoryTransact.setCreateUserId(currentUser.getUserId());
        inventoryTransact.setCreateUserName(currentUser.getUserName());
        inventoryTransact.setCreateTime(DateUtil.now());
        //库存id
        inventoryTransact.setToInventoryId(pickTask.getInventoryId());

        //事务类型 这个字段很重要
//        if (StringUtils.isEmpty(pickTask.getToBoxCode())) {
            //整箱拣货
//            inventoryTransact.setTransactType(InventoryConstant.TRANSACT_TYPE_PICK);
//        } else {
            inventoryTransact.setTransactType(InventoryConstant.TRANSACT_TYPE_PICK2);
//        }
        //来源单据号
        //记录单据号
        if (ObjectUtil.isNotNull(pickTask.getSoMasterId())) {
            SoMaster soMaster = soMasterService.getById(pickTask.getSoMasterId());
            inventoryTransact.setBillNo(soMaster.getBillNo());
        }

        //Step2.保存，返回
        inventoryTransactService.save(inventoryTransact);
        return inventoryTransact;
    }


    /**
     * 根据可用库存，分配生成拣货任务
     *
     * @param soDetail      SO明细
     * @param inventoryDtos 可用库存
     */
    private void createPickTask(SoDetailDto soDetail, List<InventorySelectDto> inventoryDtos) {
        InventoryQuantity:
        for (InventorySelectDto inventorySelectDto : inventoryDtos) {
            Double canBeQuantity = inventorySelectDto.getQuantity() - inventorySelectDto.getAllotQuantity() - inventorySelectDto.getFreezeQuantity();
            //新建拣货任务
            PickTask pickTask = new PickTask(soDetail.getSoDetailId(), soDetail.getSoMasterId(), soDetail.getWareId(),
                    soDetail.getWareName(), soDetail.getOrganizationId(), soDetail.getOrganizationName(),
                    inventorySelectDto.getAreaCode(), inventorySelectDto.getAreaName(), inventorySelectDto.getShelfCode(),
                    inventorySelectDto.getShelfName(), inventorySelectDto.getCellCode(), inventorySelectDto.getCellName(),
                    inventorySelectDto.getItemCode(), inventorySelectDto.getItemName(), inventorySelectDto.getBatchId(),
                    inventorySelectDto.getBatchName(), inventorySelectDto.getBoxCode(), inventorySelectDto.getLpn(),
                    soDetail.getPackDetailId(), soDetail.getPackDescribe(), 0.0, 0,
                    inventorySelectDto.getInventoryId(), 2,
                    inventorySelectDto.getImgUrl()
            );

            //判断可分配数量
            //1)可分配数量 大于出库任务明细待分配数量 出库任务明细分配完毕
            //2)可分配数量不够完全分配  则将可分配数量全部分配出去
            if (canBeQuantity >= (soDetail.getOrderQuantity() - soDetail.getAllottedQuantity())) {
                pickTask.setPickQuantity(soDetail.getOrderQuantity() - soDetail.getAllottedQuantity());
                //记录库存已分配数量
                inventorySelectDto.setAllotQuantity(pickTask.getPickQuantity() + inventorySelectDto.getAllotQuantity());
                //记录出库任务的已分配数量
                soDetail.setAllottedQuantity(soDetail.getAllottedQuantity() + pickTask.getPickQuantity());
                inventorySelectDto.setTransRatio(1.0);
                inventoryService.updateById(inventorySelectDto);
                save(pickTask);
                break InventoryQuantity;
            } else {
                double quantity = inventorySelectDto.getQuantity() - inventorySelectDto.getAllotQuantity() - inventorySelectDto.getFreezeQuantity();
                if (quantity > 0) {
                    pickTask.setPickQuantity(quantity);
                    inventorySelectDto.setAllotQuantity(inventorySelectDto.getAllotQuantity() + quantity);
                    soDetail.setAllottedQuantity(soDetail.getAllottedQuantity() + quantity);
                    inventorySelectDto.setTransRatio(1.0);
                    inventoryService.updateById(inventorySelectDto);
                    save(pickTask);
                }
            }
        }

        //回写SO状态
        Double orderQuantity = soDetail.getOrderQuantity();
        Double allottedQuantity = soDetail.getAllottedQuantity();

        if (orderQuantity > allottedQuantity) {
            soDetail.setState(1);
        } else if (orderQuantity.equals(allottedQuantity)) {
            soDetail.setState(2);
        }
        soDetailService.updateById(soDetail);

        SoMaster soMaster = soMasterService.findBy("bill_no", soDetail.getBillNo());
        List<SoDetail> list = soDetailService.findByBillNo(soDetail.getBillNo());
        Boolean isEnd = true;
        for (SoDetail detail : list
        ) {
            if (detail.getOrderQuantity() > detail.getAllottedQuantity()) {
//                isEnd = false;  //只要库存足够,永远判断已分配   2022/5/25 22:14 徐
                isEnd = true;
                break;
            }
        }
        if (isEnd) {
            soMaster.setState(WareTaskConstant.SO_STATE_ASSIGNED); //全部分配
        } else {
            soMaster.setState(WareTaskConstant.SO_STATE_OUTOFSTOCK); //部分分配 缺货
            // cancelPickTask(soMaster.getBillNo());
        }
        soMasterService.updateById(soMaster);
    }

    private void cancelPickTask(String billNo) {
        List<SoDetail> soDetails = soDetailService.findByBillNo(billNo);
        for (SoDetail soDetail : soDetails) {
            if (soDetail.getState() == 0) {
                return;
            }
        }
        PickTaskCriteria criteria = new PickTaskCriteria();
        criteria.setBillNo(billNo);
        criteria.setState(0);
        List<PickTaskDto> tasks = pickTaskMapper.findList(criteria);
        for (PickTaskDto pickTask : tasks) {
            Integer inventoryId = pickTask.getInventoryId();
            Inventory inventory = inventoryService.getById(inventoryId);
            Double quantity = pickTask.getPickQuantity();
            inventory.setAllotQuantity(inventory.getAllotQuantity() - quantity);
            inventoryService.updateById(inventory);
            super.removeById(pickTask.getPickTaskId());
        }
        for (SoDetail soDetail : soDetails) {
            soDetail.setAllottedQuantity(0.0);
            soDetail.setState(0);
            soDetailService.updateById(soDetail);
        }

    }


}
